package parser;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class Tag implements Comparable<Tag>{

	public final String pica3;
	public final String picaPlus;
	public final String german;
	public final Repeatability repeatability;
	public final String english;
	public final String marc;
	public static final String OHNE = "-ohne-";

	char repetitionChar = 0;
	Map<Character, Indicator> subfieldMap = new HashMap<Character, Indicator>();
	
	/*
	 *  Tags, von denen Unterfelder übernommen werden.
	 *  z.B. 5102 von 5100.
	 *  Das können aber auch künstlich gebildete Tags wie 5XX sein. Das erleichtert die
	 *  Wartbarkeit, wenn Änderungen an einer ganzen Gruppe vorgenommen werden.
	 */
	
	List<Tag> inherited = new LinkedList<Tag>();
	
	/*
	 * Manche Tags (alle 5XX) können, müssen aber keine Relation enthalten.
	 * Enthalten sie keine Relation, so borgen sie vom relationierten Tag
	 * alle Unterfelder.
	 */
	Tag related = null;
	Indicator ohne = null;
	private Set<Indicator> descriptionStrings = new HashSet<Indicator>();

	//@formatter:off
	Tag(
			final String pica3,
			final String picaPlus,
			final String german,
			final Repeatability repeatability,
			final String marc,
			final String english) {
		this.pica3 = pica3;
		this.picaPlus = picaPlus;
		this.marc = marc;
		this.german = german;
		this.english = english;
		this.repeatability = repeatability;
	}

	Tag() {
		this.pica3 = "";
		this.picaPlus = "";
		this.marc = "";
		this.german = "";
		this.english = "";
		this.repeatability = null;
	}
	
	@SuppressWarnings("boxing")
	void addIndicator(final Indicator indicator) {
		subfieldMap.put(indicator.indicatorChar, indicator);
		String descStr = indicator.descriptionStr;
		if (OHNE.equals(descStr))
			this.ohne = indicator;
	}

	
	//@formatter:on
	@Override
	public String toString() {
		return pica3 + " " + german + " (" + repeatability + ")";
	}

	@SuppressWarnings("boxing")
	public Indicator getIndicator(
			final char ind,
			final boolean optionalsIncluded) {
		Indicator found = subfieldMap.get(ind);
		if (found != null)
			return found;
		for (Tag tag : inherited) {
			found = tag.getIndicator(ind, optionalsIncluded);
			if (found != null)
				return found;
		}
		if (optionalsIncluded && related != null)
			found = related.getIndicator(ind, true);
		return found;

	}

	/**
	 * Liefert die Unterfelder, die auch im pica3-Format durch 
	 * Deskriptionszeichen ersetzt werden müssen. Auch die relationierten
	 * Unterfelder (wegen Expansion).
	 * 
	 * @return Set != null, eventuell leer.
	 */
	public final Set<Indicator> getDescriptionStrings() {
		if (descriptionStrings.size() == 0) {
			// suchen:
			descriptionStrings = getAllIndicators();
			//@formatter:off
			for (Iterator<Indicator> it = descriptionStrings.iterator(); 
					it.hasNext();) {
				Indicator subfieldInfo = it.next();
				if (subfieldInfo.descriptionStr == null)
					it.remove(); // sicheres Entfernen!
			}
			//@formatter:on
			Indicator ohneTemp = getOhne();
			descriptionStrings.remove(ohneTemp);
		}
		return descriptionStrings;
	}

	/**
	 * Liefert das Unterfeld, das im pica3-Format ohne Indikator am Anfang
	 * stehen darf/muss.
	 * 
	 * @return Indicator eventuell null.
	 */
	public final Indicator getOhne() {
		Indicator sub = this.ohne;
		if (sub != null)
			return sub;
		for (Tag tag : inherited) {
			sub = tag.getOhne();
			if (sub != null)
				return sub;
		}
		if (related == null)
			return null;
		else
			return related.ohne;
	}

	/**
	 * Liefert die eigenen Unterfelder, also genau die, die auch bei einer
	 * echten Relation auftauchen dürfen.
	 * 
	 * @return Set != null, eventuell leer.
	 */
	public Set<Indicator> getOwnIndicators() {
		/*
		 * wichtig: neue Menge als Basis für 
		 * getAllFields() und getDescriptionsSigns()
		 */
		Set<Indicator> subs =
			new HashSet<Indicator>(subfieldMap.values());
		for (Tag tag : inherited) {
//			System.err.println("\n" + this + " inherited: " + tag);
			subs.addAll(tag.getOwnIndicators());
		}
		return subs;
	}

	/**
	 * Liefert alle Unterfelder, auch die relationierten.
	 * 
	 * @return Set != null, eventuell leer.
	 */
	public Set<Indicator> getAllIndicators() {
		Set<Indicator> subs = getOwnIndicators();
		if (related != null) {
			subs.addAll(related.getOwnIndicators());
		}
		return subs;
	}

	/**
	 * @return the repetitionChar
	 */
	public char getRepetitionChar() {
		return repetitionChar;
	}

	@Override
	public int compareTo(Tag o) {
		int a1 = Integer.parseInt(this.pica3);
		int a2 = Integer.parseInt(o.pica3);
		return a1 - a2;
	}

}
